#![allow(non_snake_case)]
use std::{cmp::Ordering, mem};

use super::get_cell_border_class;
use crate::{
    table::{TableBody, TableHeader}, TableFooter, TableState
};
use leptos::prelude::*;
use serde_json::{Map, Value};
use shq_common::prelude::{edit_props::EditProps, Column, ColumnCells, Direction, TableFooterShowProps};
use thaw_utils::class_list;
#[component]
pub fn ListTable(
    ///表格列头
    #[prop(into)]
    header_cells: Memo<ColumnCells>,
    ///是否多选
    multi_sel: bool,
    ///表格页脚属性
    #[prop(optional, into)]
    footer_show_props: TableFooterShowProps,
    ///是否显示边框
    #[prop(optional, into)]
    cell_border: bool,
    ///是否可以更新
    can_update: bool,
) -> impl IntoView {
    let table_state =
        use_context::<ReadSignal<TableState>>().expect("ReadSignal<TableState>在上下文中不存在");
    let page = table_state.with_untracked(|ts| ts.page());
    let page_size = table_state.with_untracked(|ts| ts.page_size());
    let orig_list_data = table_state.with_untracked(|ts| ts.list_data());
    let sel_indexs = table_state.with_untracked(|ts| ts.selected_indexs());
    let list_indexs: RwSignal<Vec<usize>> = RwSignal::new(vec![]);
    Effect::new(move |_| {
        let (filters, orders) = table_state.with_untracked(|ts| (ts.all_filters(), ts.all_orders()));
        let has_filter = filters.values().any(|w| w.with(|w| w.is_some()));
        let has_order = orders.values().any(|w| w.with(|w| w.is_some()));
        let indexs = orig_list_data.with_untracked(|ld| {
            let filtered: Vec<(usize, &Value)> = if has_filter {
                ld.iter()
                    .enumerate()
                    .filter(|(_i, val)| {
                        filters.values().all(|w| {
                            w.with(|w| {
                                w.as_ref()
                                    .map(|w| {
                                        let idx = table_state.with_untracked(|ts| {
                                            ts.get_col_index(w.field_name.as_str())
                                        });
                                        let dict_col_name = header_cells.with_untracked(|hc| {
                                            let col = hc.get_leaf_cell(w.field_name.as_str());
                                            col.map(|c| c.dict_col_name.clone()).flatten()
                                        });
                                        let name = dict_col_name.as_ref().unwrap_or(&w.field_name);
                                        let v = get_cell_val(name, val, idx);
                                        v.map(|v| w.is_ok(v)).unwrap_or(false)
                                    })
                                    .unwrap_or(false)
                            })
                        })
                    })
                    .map(|(i, v)| (i, v))
                    .collect()
            } else {
                ld.iter().enumerate().collect()
            };
            let ordered = if has_order {
                let mut ds = filtered;
                ds.sort_by(|(_i1, a), (_i2, b)| {
                    let ord = orders.iter().filter(|(_, d)| d.get().is_some()).fold(
                        Ordering::Equal,
                        |acc, (n, d)| {
                            let idx = table_state.with_untracked(|ts| ts.get_col_index(n));
                            let dict_col_name = header_cells.with_untracked(|hc| {
                                let col = hc.get_leaf_cell(n);
                                col.map(|c| c.dict_col_name.clone()).flatten()
                            });
                            let name = dict_col_name.as_ref().unwrap_or(n);
                            let v1 = get_cell_val(name, a, idx);
                            let v2 = get_cell_val(name, b, idx);
                            acc.then(order_val(v1, v2, d.get().unwrap_or(Direction::Asc)))
                        },
                    );
                    ord
                });
                ds
            } else {
                filtered
            };
            ordered.iter().map(|(i, _v)| *i).collect()
        });
        page.set(1);
        sel_indexs.set(vec![]);
        list_indexs.set(indexs);
    });
    let total = Signal::derive(move || list_indexs.with(|ld| ld.len()));
    let body_view = move || {
        let psize = page_size.get();
        let mut start = (page.get() - 1) * psize;
        let mut end = start + psize;
        let total = total.get();
        if start >= total {
            start = 0;
            end = psize;
        }
        if end > total {
            end = total
        }
        let list_indexs = Signal::derive(move || {
            list_indexs.with(|li| {
                let mut idxs: Vec<usize> = vec![];
                for i in start..end {
                    idxs.push(li[i]);
                }
                idxs
            })
        });
        view! {
            <TableBody header_cells cell_border multi_sel list_indexs table_state can_update/>
        }
    };
    let get_width = move || {
        header_cells.with(|hc| {
            let leafs = hc.get_leaf_cells();
            let mut width: u32 = 0;
            for l in leafs.iter().filter(|l| l.title.is_some()) {
                width += l
                    .list_props
                    .as_ref()
                    .map(|lp| lp.width as u32)
                    .unwrap_or(100);
            }
            if multi_sel {
                width + 60
            } else {
                width
            }
        })
    };
    view! {
        <div class="flex-auto bg-base-100 overflow-auto">
            <table class=class_list!("table table-md table-pin-rows table-pin-cols table-fixed",get_cell_border_class(cell_border)) style:width=move || format!("{}px", get_width())>
                <TableHeader header_cells table_state cell_border multi_sel/>
                {body_view}
            </table>
        </div>
        <TableFooter total=total show_props=footer_show_props is_client=true/>
    }.into_any()
}

fn order_val(val1: Option<&Value>, val2: Option<&Value>, dire: Direction) -> Ordering {
    if val1.is_none() && val2.is_none() {
        return Ordering::Equal;
    } else if val1.is_some() && val2.is_none() {
        return Ordering::Greater;
    } else if val1.is_none() && val2.is_some() {
        return Ordering::Less;
    }
    let val1 = val1.unwrap();
    let val2 = val2.unwrap();
    match val1 {
        Value::Bool(bv) => {
            let bv2 = val2.as_bool().unwrap_or(false);
            match dire {
                Direction::Asc => bv.cmp(&bv2),
                Direction::Desc => bv2.cmp(&bv),
            }
        }
        Value::Number(nv) => {
            if nv.is_u64() {
                let nv = nv.as_u64().unwrap_or(0);
                let nv2 = val2.as_u64().unwrap_or(0);
                match dire {
                    Direction::Asc => nv.cmp(&nv2),
                    Direction::Desc => nv2.cmp(&nv),
                }
            } else if nv.is_i64() {
                let nv = nv.as_i64().unwrap_or(0);
                let nv2 = val2.as_i64().unwrap_or(0);
                match dire {
                    Direction::Asc => nv.cmp(&nv2),
                    Direction::Desc => nv2.cmp(&nv),
                }
            } else {
                let nv = nv.as_f64().unwrap_or(0.0);
                let nv2 = val2.as_f64().unwrap_or(0.0);
                match dire {
                    Direction::Asc => nv.partial_cmp(&nv2).unwrap_or(Ordering::Equal),
                    Direction::Desc => nv2.partial_cmp(&nv).unwrap_or(Ordering::Equal),
                }
            }
        }
        Value::String(sv) => {
            let sv2 = val2.as_str().unwrap_or("");
            match dire {
                Direction::Asc => sv.as_str().cmp(sv2),
                Direction::Desc => sv2.cmp(sv),
            }
        }
        Value::Null | Value::Array(_) | Value::Object(_) => Ordering::Equal,
    }
}

fn get_cell_val<'a>(
    col_name: &'a str,
    val: &'a Value,
    col_idx: Option<usize>,
) -> Option<&'a Value> {
    if let Some(mval) = val.as_object() {
        mval.get(col_name)
    } else if let Some(vals) = val.as_array() {
        col_idx.map(|idx| vals.get(idx)).flatten()
    } else {
        None
    }
}
pub struct EditColValue {
    pub row_index: usize,
    pub col_index: usize,
    pub value: RwSignal<Option<Value>>,
    pub title: RwSignal<Option<String>>,
    pub err_msg: RwSignal<Option<String>>,
}
pub struct EditColValues {
    pub edit_cols: Vec<(String, EditProps)>,
    pub values: Vec<EditColValue>,
}
impl EditColValues {
    pub fn from(cols: &[Column], list_count: usize) -> Self {
        let edit_col_props: Vec<(String, EditProps)> = cols
            .iter()
            .filter(|col| {
                col.edit_props
                    .as_ref()
                    .map(|ep| ep.is_editable)
                    .unwrap_or(false)
                    && col.list_props.as_ref().map(|lp| lp.edit).unwrap_or(false)
            })
            .map(|col| {
                (
                    col.name.clone().unwrap_or_default(),
                    col.edit_props.clone().unwrap(),
                )
            })
            .collect();
        let plen = edit_col_props.len();
        let mut values: Vec<EditColValue> = Vec::new();
        for i in 0..list_count {
            for j in 0..plen {
                values.push(EditColValue {
                    row_index: i,
                    col_index: j,
                    value: RwSignal::new(None),
                    title: RwSignal::new(None),
                    err_msg: RwSignal::new(None),
                });
            }
        }
        EditColValues {
            edit_cols: edit_col_props,
            values,
        }
    }
    pub fn get_edit_col(&self, col_name: &str) -> Option<(usize, &EditProps)> {
        let v = self
            .edit_cols
            .iter()
            .enumerate()
            .find(|(_, (name, _))| name == col_name);
        v.map(|(i, (_, ep))| (i, ep))
    }
    pub fn get_value(&self, row_index: usize, col_index: usize) -> RwSignal<Option<Value>> {
        self.values
            .iter()
            .find(|v| v.row_index == row_index && v.col_index == col_index)
            .map(|v| v.value.clone())
            .unwrap()
    }
    pub fn get_title(&self, row_index: usize, col_index: usize) -> RwSignal<Option<String>> {
        self.values
            .iter()
            .find(|v| v.row_index == row_index && v.col_index == col_index)
            .map(|v| v.title)
            .unwrap()
    }
    pub fn get_err_msg(&self, row_index: usize, col_index: usize) -> RwSignal<Option<String>> {
        self.values
            .iter()
            .find(|v| v.row_index == row_index && v.col_index == col_index)
            .map(|v| v.err_msg)
            .unwrap()
    }
    pub fn clear_error_msgs(&self) {
        for v in self.values.iter() {
            v.err_msg.set(None);
        }
    }
    pub fn set_value(&self, row_index: usize, col_name: &str, value: Value) {
        let col_index = self
            .edit_cols
            .iter()
            .enumerate()
            .find(|(_, (name, _))| name == col_name)
            .map(|ep| ep.0);
        if col_index.is_none() {
            return;
        }
        let col_index = col_index.unwrap();
        let val = self
            .values
            .iter()
            .find(|v| row_index == v.row_index && col_index == v.col_index)
            .map(|v| v.value);
        if let Some(v) = val {
            v.set(Some(value));
        }
    }
    pub fn set_title(&self, row_index: usize, col_name: &str, title: Option<String>) {
        let idx = self
            .edit_cols
            .iter()
            .enumerate()
            .find(|(_, (name, _))| name == col_name)
            .map(|ep| ep.0);
        if let Some(col_index) = idx {
            if let Some(v) = self
                .values
                .iter()
                .find(|v| v.row_index == row_index && v.col_index == col_index)
            {
                v.title.set(title);
            }
        }
    }
    pub fn set_error_msg(&self, row_index: usize, col_name: &str, msg: Option<String>) {
        let idx = self
            .edit_cols
            .iter()
            .enumerate()
            .find(|(_, (name, _))| name == col_name)
            .map(|ep| ep.0);
        let col_index = idx.unwrap_or(0);
        if let Some(v) = self
            .values
            .iter()
            .find(|v| v.row_index == row_index && v.col_index == col_index)
        {
            v.err_msg.set(msg);
        }
    }
    pub fn get_update_rows(&self, pk_col: &str, list_data: &[Value]) -> Vec<Map<String, Value>> {
        let mut rows: Vec<(usize, usize, Value)> = self
            .values
            .iter()
            .filter(|v| v.value.with(|v| v.is_some()))
            .map(|v| {
                let val = v.value.get_untracked().unwrap();
                (v.row_index, v.col_index, val)
            })
            .collect();
        rows.sort_by(|a, b| {
            let i_cmp = a.0.cmp(&b.0);
            if i_cmp == Ordering::Equal {
                a.1.cmp(&b.1)
            } else {
                i_cmp
            }
        });
        let mut update_rows: Vec<Map<String, Value>> = Vec::new();
        let mut pi: usize = 0;
        let mut new_row: Map<String, Value> = Map::new();
        for (i, j, val) in rows.into_iter() {
            if pi != i && !new_row.is_empty() {
                let mut new_row2 = Map::new();
                mem::swap(&mut new_row, &mut new_row2);
                update_rows.push(new_row2);
            }
            let row = list_data[i].as_object().expect("list_data不是对象");
            if new_row.is_empty() {
                let pk_val = row.get(pk_col).expect("list_data中没有主键列").clone();
                new_row.insert(pk_col.to_string(), pk_val);
            }
            let (col_name, _) = self.edit_cols[j].as_borrowed();
            new_row.insert(col_name.to_string(), val);
            pi = i;
        }
        if !new_row.is_empty() {
            update_rows.push(new_row);
        }
        update_rows
    }
}
